#ifndef MULTICONJ_MATERIALES_H
#define MULTICONJ_MATERIALES_H

#include <iostream>
#include "lista.h"
#include "defines.h"

using namespace std;


struct tupla{
	material mat;
	nat cantidad;
	
	bool operator==(const tupla& tup) const{
		return (mat==tup.mat && cantidad==tup.cantidad);
	}
	//esto es solo para testing! no se deben usar..
	void mostrarTupla(ostream& os) const{
		os << "<" << mat << ";" << cantidad << ">";
	}
};

ostream& operator<<(ostream& out, const tupla& t) {
	t.mostrarTupla(out);
	return out;
}

class Multiconj_materiales{
	public:

        class IterMateriales{
            public:
                IterMateriales();
                IterMateriales(const Multiconj_materiales*);
                ~IterMateriales();
                bool tieneProximo() const;
                material avanzar();
            private:
                Lista<tupla>::IterLista_const it;
        };

		Multiconj_materiales():multiconj_material(Lista<tupla>()){}
		~Multiconj_materiales(){}
		bool vacio() const;
		void agregar(material m, nat c);
		void sacar(material m, nat c);
		bool esta(material m) const;
		nat cardinal(material m) const;
		IterMateriales crearIterMateriales() const;

		//esto es solo para testing! no se deben usar..
		void mostrarMulticonj_materiales(ostream&) const;
		bool operator==(const Multiconj_materiales& ms) const;

	private:
		Lista<tupla> multiconj_material;
};

/******************************************************************************************/
/*                                FUNCIONES DE Multiconj                                  */
/******************************************************************************************/

bool Multiconj_materiales::vacio() const{
	return multiconj_material.esVacia();
}

void Multiconj_materiales::agregar(material m, nat c){
	bool esta = false;
	Lista<tupla>::IterLista it = multiconj_material.crearIt();
	while(it.tieneProximo()){
		if(it.actual().mat==m){
			esta=true;
			it.actual().cantidad = it.actual().cantidad + c;
		}
		it.avanzar();
	}
	if(!esta){
		if(c>0){
			tupla temp;
			temp.mat=m;
			temp.cantidad=c;
			multiconj_material.agregarAtras(temp);
		}
	}
}

bool Multiconj_materiales::esta(material m) const{
	bool esta = false;
	Lista<tupla>::IterLista_const it = multiconj_material.crearIt_const();
	while(it.tieneProximo() && !esta){
		if(it.actual().mat==m){
			esta=true;
		}
		it.avanzar();
	}
	return esta;
}


nat Multiconj_materiales::cardinal(material m) const{
	nat res = 0;
	Lista<tupla>::IterLista_const it = multiconj_material.crearIt_const();
	while(it.tieneProximo() && res==0){
		if(it.actual().mat==m){
			res = it.actual().cantidad;
		}
		it.avanzar();
	}
	return res;
}

void Multiconj_materiales::sacar(material m, nat c){
	Lista<tupla>::IterLista it = multiconj_material.crearIt();
	while(it.tieneProximo()){
		if(it.actual().mat==m){
			if(c>=it.actual().cantidad){
				it.eliminar();
			}else{
				it.actual().cantidad = it.actual().cantidad -c;
			}
			break;
		}
		it.avanzar();
	}
}

Multiconj_materiales::IterMateriales Multiconj_materiales::crearIterMateriales() const{
	return IterMateriales(this);
}


void Multiconj_materiales::mostrarMulticonj_materiales(ostream& os) const{
	multiconj_material.mostrarLista(os);
}

/******************************************************************************************/
/*                             FUNCIONES DE iterMateriales                                */
/******************************************************************************************/

Multiconj_materiales::IterMateriales::IterMateriales(){}

Multiconj_materiales::IterMateriales::IterMateriales(const Multiconj_materiales* ms){
    it = ms->multiconj_material.crearIt_const();
}

Multiconj_materiales::IterMateriales::~IterMateriales(){}

bool Multiconj_materiales::IterMateriales::tieneProximo() const{
    return it.tieneProximo();
}

material Multiconj_materiales::IterMateriales::avanzar(){
    material actual = it.actual().mat;
    it.avanzar();
    return actual;
}

/******************************************************************************************/
/*                                         OTRAS                                          */
/******************************************************************************************/

bool Multiconj_materiales::operator==(const Multiconj_materiales& ms) const {
	bool res = ms.multiconj_material.longitud() == multiconj_material.longitud();
	if(res){
		Lista<tupla>::IterLista_const it1 = multiconj_material.crearIt_const();
		while(it1.tieneProximo()){
			if(!ms.multiconj_material.esta(it1.actual())){ res = false;}
			it1.avanzar();
		}
	}
	return res;
}

ostream& operator<<(ostream& out, const Multiconj_materiales& ms) {
	ms.mostrarMulticonj_materiales(out);
	return out;
}

#endif
